<!DOCTYPE html>
<html lang="zh-CN,en,zh-TW,default">
  <head hexo-theme='https://github.com/volantis-x/hexo-theme-volantis/tree/4.2.0'>
  <meta charset="utf-8">
  <!-- SEO相关 -->
  
    
  
  <!-- 渲染优化 -->
  <meta http-equiv='x-dns-prefetch-control' content='on' />
  <link rel='dns-prefetch' href='https://cdn.jsdelivr.net'>
  <link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
  <meta name="renderer" content="webkit">
  <meta name="force-rendering" content="webkit">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1">
  <meta name="HandheldFriendly" content="True" >
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  <!-- 页面元数据 -->
  
  <title>有关String的几个问题（结合JVM） - Yayako&#39;s Blog</title>
  
    <meta name="keywords" content="Java,String">
  

  
    <meta name="description" content="为什么String是不可变的？String的几种创建方式有什么不同？“+”拼接String的底层原理？String与StringBuilder与StringBuffer的区别？">
  

  <!-- feed -->
  

  <!-- import meta -->
  

  <!-- link -->
  
    <link rel="shortcut icon" type='image/x-icon' href="https://halo-yayako.oss-cn-hangzhou.aliyuncs.com/icons8-lamb-96_1600147384617.png">
  

  <!-- import link -->
  

  
    
<link rel="stylesheet" href="/css/first.css">

  

  
  <link rel="stylesheet" href="/css/style.css" media="print" onload="this.media='all';this.onload=null">
  <noscript><link rel="stylesheet" href="/css/style.css"></noscript>
  

  <script id="loadcss"></script>

  

<script>if (/*@cc_on!@*/false || (!!window.MSInputMethodContext && !!document.documentMode)) document.write('\x3Cscript id="_iealwn_js" data-outver="11" src="https://support.dmeng.net/ie-alert-warning/latest.js">\x3C/script>'); </script>

</head>

  <body>
    

<header id="l_header" class="l_header always shadow blur "  >
  <div class='container'>
  <div id='wrapper'>
    <div class='nav-sub'>
      <p class="title"></p>
      <ul class='switcher nav-list-h m-phone' id="pjax-header-nav-list">
        <li><a id="s-comment" class="fas fa-comments fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
          <li><a id="s-toc" class="s-toc fas fa-list fa-fw" target="_self" href='javascript:void(0)'></a></li>
        
      </ul>
    </div>
		<div class="nav-main">
      
        
        <a class="title flat-box" target="_self" href='/'>
          
            <img no-lazy class='logo' src='https://yayako-blog.oss-cn-hangzhou.aliyuncs.com/settings/icons8-doughnut-100.png'/>
          
          
          
        </a>
      

			<div class='menu navigation'>
				<ul class='nav-list-h m-pc'>
          
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Java/
                  
                  
                  
                    id="categoriesJava"
                  >
                  Java
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Java/%E5%9F%BA%E7%A1%80/
                  
                  
                  
                    id="categoriesJavaE59FBAE7A180"
                  >
                  基础
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Java/JVM/
                  
                  
                  
                    id="categoriesJavaJVM"
                  >
                  JVM
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Java/J.U.C/
                  
                  
                  
                    id="categoriesJavaJUC"
                  >
                  J.U.C
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Golang/
                  
                  
                  
                    id="categoriesGolang"
                  >
                  Golang
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Golang/%E5%9F%BA%E7%A1%80
                  
                  
                  
                    id="categoriesGolangE59FBAE7A180"
                  >
                  基础
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E6%A1%86%E6%9E%B6/
                  
                  
                  
                    id="categoriesE6A186E69EB6"
                  >
                  框架
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E6%A1%86%E6%9E%B6/SSM/
                  
                  
                  
                    id="categoriesE6A186E69EB6SSM"
                  >
                  SSM
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/
                  
                  
                  
                    id="categoriesE4B8ADE997B4E4BBB6"
                  >
                  中间件
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/Redis/
                  
                  
                  
                    id="categoriesE4B8ADE997B4E4BBB6Redis"
                  >
                  Redis
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/MySQL/
                  
                  
                  
                    id="categoriesMySQL"
                  >
                  MySQL
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/MySQL/%E5%9F%BA%E7%A1%80/
                  
                  
                  
                    id="categoriesMySQLE59FBAE7A180"
                  >
                  基础
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/MySQL/InnoDB/
                  
                  
                  
                    id="categoriesMySQLInnoDB"
                  >
                  InnoDB
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E7%AE%97%E6%B3%95/
                  
                  
                  
                    id="categoriesE7AE97E6B395"
                  >
                  算法
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E7%AE%97%E6%B3%95/LeetCode/
                  
                  
                  
                    id="categoriesE7AE97E6B395LeetCode"
                  >
                  LeetCode
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E7%AE%97%E6%B3%95/%E5%89%91%E6%8C%87offer/
                  
                  
                  
                    id="categoriesE7AE97E6B395E58991E68C87offer"
                  >
                  剑指offer
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%B7%A5%E5%85%B7/
                  
                  
                  
                    id="categoriesE5B7A5E585B7"
                  >
                  工具
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%B7%A5%E5%85%B7/Linux/
                  
                  
                  
                    id="categoriesE5B7A5E585B7Linux"
                  >
                  Linux
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%B7%A5%E5%85%B7/Nginx/
                  
                  
                  
                    id="categoriesE5B7A5E585B7Nginx"
                  >
                  Nginx
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%B7%A5%E5%85%B7/Docker/
                  
                  
                  
                    id="categoriesE5B7A5E585B7Docker"
                  >
                  Docker
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
          
            
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%85%B6%E4%BB%96/
                  
                  
                  
                    id="categoriesE585B6E4BB96"
                  >
                  其他
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%85%B6%E4%BB%96/%E9%9D%A2%E7%BB%8F/
                  
                  
                  
                    id="categoriesE585B6E4BB96E99DA2E7BB8F"
                  >
                  面经
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
          
				</ul>
			</div>

      <div class="m_search">
        <form name="searchform" class="form u-search-form">
          <i class="icon fas fa-search fa-fw"></i>
          <input type="text" class="input u-search-input" placeholder="Search..." />
        </form>
      </div>

			<ul class='switcher nav-list-h m-phone'>
				
					<li><a class="s-search fas fa-search fa-fw" target="_self" href='javascript:void(0)'></a></li>
				
				<li>
          <a class="s-menu fas fa-bars fa-fw" target="_self" href='javascript:void(0)'></a>
          <ul class="menu-phone list-v navigation white-box">
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Java/
                  
                  
                  
                    id="categoriesJava"
                  >
                  Java
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Java/%E5%9F%BA%E7%A1%80/
                  
                  
                  
                    id="categoriesJavaE59FBAE7A180"
                  >
                  基础
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Java/JVM/
                  
                  
                  
                    id="categoriesJavaJVM"
                  >
                  JVM
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Java/J.U.C/
                  
                  
                  
                    id="categoriesJavaJUC"
                  >
                  J.U.C
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Golang/
                  
                  
                  
                    id="categoriesGolang"
                  >
                  Golang
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/Golang/%E5%9F%BA%E7%A1%80
                  
                  
                  
                    id="categoriesGolangE59FBAE7A180"
                  >
                  基础
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E6%A1%86%E6%9E%B6/
                  
                  
                  
                    id="categoriesE6A186E69EB6"
                  >
                  框架
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E6%A1%86%E6%9E%B6/SSM/
                  
                  
                  
                    id="categoriesE6A186E69EB6SSM"
                  >
                  SSM
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/
                  
                  
                  
                    id="categoriesE4B8ADE997B4E4BBB6"
                  >
                  中间件
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E4%B8%AD%E9%97%B4%E4%BB%B6/Redis/
                  
                  
                  
                    id="categoriesE4B8ADE997B4E4BBB6Redis"
                  >
                  Redis
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/MySQL/
                  
                  
                  
                    id="categoriesMySQL"
                  >
                  MySQL
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/MySQL/%E5%9F%BA%E7%A1%80/
                  
                  
                  
                    id="categoriesMySQLE59FBAE7A180"
                  >
                  基础
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/MySQL/InnoDB/
                  
                  
                  
                    id="categoriesMySQLInnoDB"
                  >
                  InnoDB
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E7%AE%97%E6%B3%95/
                  
                  
                  
                    id="categoriesE7AE97E6B395"
                  >
                  算法
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E7%AE%97%E6%B3%95/LeetCode/
                  
                  
                  
                    id="categoriesE7AE97E6B395LeetCode"
                  >
                  LeetCode
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E7%AE%97%E6%B3%95/%E5%89%91%E6%8C%87offer/
                  
                  
                  
                    id="categoriesE7AE97E6B395E58991E68C87offer"
                  >
                  剑指offer
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%B7%A5%E5%85%B7/
                  
                  
                  
                    id="categoriesE5B7A5E585B7"
                  >
                  工具
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%B7%A5%E5%85%B7/Linux/
                  
                  
                  
                    id="categoriesE5B7A5E585B7Linux"
                  >
                  Linux
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%B7%A5%E5%85%B7/Nginx/
                  
                  
                  
                    id="categoriesE5B7A5E585B7Nginx"
                  >
                  Nginx
                </a>
                
              </li>
            
          
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%B7%A5%E5%85%B7/Docker/
                  
                  
                  
                    id="categoriesE5B7A5E585B7Docker"
                  >
                  Docker
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
            
              
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%85%B6%E4%BB%96/
                  
                  
                  
                    id="categoriesE585B6E4BB96"
                  >
                  其他
                </a>
                
                  <ul class="list-v">
                    
                      
            
              <li>
                <a class="menuitem flat-box faa-parent animated-hover" href=/categories/%E5%85%B6%E4%BB%96/%E9%9D%A2%E7%BB%8F/
                  
                  
                  
                    id="categoriesE585B6E4BB96E99DA2E7BB8F"
                  >
                  面经
                </a>
                
              </li>
            
          
                    
                  </ul>
                
              </li>
            
          
            
          </ul>
        </li>
			</ul>
		</div>
	</div>
  </div>
</header>

    <div id="l_body">
      <div id="l_cover">
  
    
        <div id="half" class='cover-wrapper post search' style="display: ;">
          
            <div id='cover-backstretch'></div>
          
          <div class='cover-body'>
  <div class='top'>
    
    
      <p class="title">Yayako's Blog</p>
    
    
      <p class="subtitle">「Shooting for the stars when I couldn't make a killing.」</p>
    
  </div>
  <div class='bottom'>
    
      <div class="m_search">
        <form name="searchform" class="form u-search-form">
          <input type="text" class="input u-search-input" placeholder="Have a nice day!" />
          <i class="icon fas fa-search fa-fw"></i>
        </form>
      </div>
    
    <div class='menu navigation'>
      <div class='list-h'>
        
          
            <a href="/"
              
              
              id="home">
              <i class='fas fa-home fa-fw'></i><p>首页</p>
            </a>
          
            <a href="/categories/"
              
              
              id="categories">
              <i class='fas fa-folder-open fa-fw'></i><p>分类</p>
            </a>
          
            <a href="/archives/"
              
              
              id="archives">
              <i class='fas fa-archive fa-fw'></i><p>归档</p>
            </a>
          
            <a href="/tags/"
              
              
              id="tags">
              <i class='fas fa-tags fa-fw'></i><p>标签</p>
            </a>
          
            <a href="/friends/"
              
              
              id="friends">
              <i class='fas fa-link fa-fw'></i><p>友链</p>
            </a>
          
            <a href="/about/"
              
              
              id="about">
              <i class='fas fa-user fa-fw'></i><p>关于</p>
            </a>
          
        
      </div>
    </div>
  </div>
</div>

          <div id="scroll-down" style="display: none;"><i class="fa fa-chevron-down scroll-down-effects"></i></div>
        </div>
    
  
  </div>

      <div id="safearea">
        <div class="body-wrapper" id="pjax-container">
          

<div class='l_main'>
  <article class="article post white-box reveal md shadow article-type-post" id="post" itemscope itemprop="blogPost">
  


  
  <div class="article-meta" id="top">
    
    
    
      <h1 class="title">
        有关String的几个问题（结合JVM）
      </h1>
      <div class='new-meta-box'>
        
          
            
<div class='new-meta-item author'>
  <a class='author' href="/about" rel="nofollow">
    <img no-lazy src="https://yayako-blog.oss-cn-hangzhou.aliyuncs.com/settings/icons8-cat-100%20%282%29.png">
    <p>yayako</p>
  </a>
</div>

          
        
          
            <div class="new-meta-item date">
  <a class='notlink'>
    <i class="fas fa-calendar-alt fa-fw" aria-hidden="true"></i>
    <p>发布于：2020年1月22日</p>
  </a>
</div>

          
        
          
            
  <div class="new-meta-item browse leancloud">
    <a class='notlink'>
      
      <div id="lc-pv" data-title="有关String的几个问题（结合JVM）" data-path="/2020/01/22/%E6%9C%89%E5%85%B3String%E7%9A%84%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%EF%BC%88%E7%BB%93%E5%90%88JVM%EF%BC%89/">
        <i class="fas fa-eye fa-fw" aria-hidden="true"></i>
        <span id='number'><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span>
        次浏览
      </div>
    </a>
  </div>


          
        
          
            
<div class="new-meta-item comments-count">
  
  <a href="/2020/01/22/%E6%9C%89%E5%85%B3String%E7%9A%84%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%EF%BC%88%E7%BB%93%E5%90%88JVM%EF%BC%89/#comments">
    <i class="fas fa-comment-dots fa-fw"></i>
    <span class="valine-comment-count" data-xid="/2020/01/22/%E6%9C%89%E5%85%B3String%E7%9A%84%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%EF%BC%88%E7%BB%93%E5%90%88JVM%EF%BC%89/">0</span>
    <span class="leancloud-comments-count">&nbsp;条评论</span>
  </a>
</div>


          
        
      </div>
    
  </div>


  
  
  <h2 id="有关String的几个问题（结合JVM）"><a href="#有关String的几个问题（结合JVM）" class="headerlink" title="有关String的几个问题（结合JVM）"></a>有关String的几个问题（结合JVM）</h2><p>首先明确<strong>字符串常量存储的位置</strong>，字符串在Java虚拟机中是存储在字符串常量池中的，而在jdk7之前，字符串常量池存在于永久代（也就是方法区）中，在jdk7开始，字符串常量池被移入了Java堆。</p>
<p>而对象，是存储在Java堆中的。</p>
<h3 id="为什么String是不可变的？"><a href="#为什么String是不可变的？" class="headerlink" title="为什么String是不可变的？"></a>为什么String是不可变的？</h3><p>首先要知道，在实际开发中，经常会与字符串打交道。如果我们在开发过程中用到一个就建一个，将会大大的增大<strong>性能的开销</strong>，因此设计者采用了<strong>共享字符串</strong>的做法，将字符串常量放入字符串常量池中，为各线程共享，相同内容的字符串可以指向同一个字符串对象，这样就做到了内存的消耗降低。</p>
<h4 id="怎么知道它是不可变的？"><a href="#怎么知道它是不可变的？" class="headerlink" title="怎么知道它是不可变的？"></a>怎么知道它是不可变的？</h4><p>翻开String源码（jdk8）就可以看到：</p>
<ul>
<li>String类被final关键字修饰</li>
<li>关键成员变量value被final关键字修饰</li>
</ul>
<p><img src="https://typora-yayako.oss-cn-hangzhou.aliyuncs.com/image-20200325175031185.png" class="lazyload" data-srcset="https://typora-yayako.oss-cn-hangzhou.aliyuncs.com/image-20200325175031185.png" srcset="" alt="image-20200325175031185"></p>
<p>而被final关键字修饰的变量一旦被赋值就不可再改变。被修饰的类则不可以被继承、重写。</p>
<p>举个栗子，如下这段代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    String s1 = <span class="keyword">new</span> String(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">    appendStr(s1);</span><br><span class="line">    System.out.println(s1); <span class="comment">// a</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> String <span class="title">appendStr</span><span class="params">(String s)</span> </span>&#123;</span><br><span class="line">    s += <span class="string">&quot;b&quot;</span>;</span><br><span class="line">    <span class="keyword">return</span> s;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>最终程序输出了“a”，即s1原封不动。</p>
<p>当把s1传给appendStr方法时，实际传递的是复制了一份s1的引用，他在appendStr中有了名字“s”，appendStr方法从头到尾只对这个复制的引用进行操作，而原来的s1一直指向原来的对象。这正是String类不可改变的实现。</p>
<p>而当你用StringBuffer或StringBuilder来说现时，会有不一样的答案，因为这两个都继承了AbstractStringBuilder类，而他的value数组是可变的。</p>
<h4 id="为什么要用final修饰？"><a href="#为什么要用final修饰？" class="headerlink" title="为什么要用final修饰？"></a>为什么要用final修饰？</h4><p>为了安全。</p>
<h5 id="用final修饰域"><a href="#用final修饰域" class="headerlink" title="用final修饰域"></a>用final修饰域</h5><p>我们已经知道字符串常量是线程共享的，那么假设String是可变的，则当多个线程同时对字符串常量池中的同一对象进行修改时，将会发生不可预测的结果。而不可变的特性正规避了这不安全的点。</p>
<h5 id="用final修饰类"><a href="#用final修饰类" class="headerlink" title="用final修饰类"></a>用final修饰类</h5><p>防止String被继承、重写。</p>
<blockquote>
<p>引用自 <a target="_blank" rel="noopener" href="https://blog.csdn.net/u014745069/article/details/86612841?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task">当面试官问我————为什么String是final的？</a></p>
<p>如果String可以被继承，那么势必就会有人通过创建String引用并指向String子类对象的方式，来使用子类的方法，比如像这样写：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">String aa = <span class="keyword">new</span> SubString(<span class="string">&quot;abcd&quot;</span>);</span><br><span class="line">aa.length();</span><br></pre></td></tr></table></figure>

<p>这看似没有什么问题，但是问题在于性能，前面提到了，在程序开发过程中，字符串对象是非常常用的，上述代码在调用对象aa.length() 时，虚拟机就会去虚函数表中查找并判定究竟是应该调用哪个子类的length()方法。在大量使用字符串对象的场景下，势必会降低程序运行效率。</p>
<p>其次又是安全性，这个安全性的解释为语义的安全性，面向对象的语言本身就要求要有清晰的语义和明确的表达。String的各个方法都围绕着一个char数组进行，所有方法的语义都是最直接、最有效的。重写String的方法意味着：不一样的语义或者错误的语义。这将直接导致String行为的不确定性，使用String对象的代码将会是不安全的代码。因此，Java设计者才会禁止任何人继承String类，主要是为了String对象的操作语义不被改变，确保使用String对象的代码是绝对安全的。</p>
</blockquote>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>所以总结来说就是兼顾<strong>性能</strong>和<strong>安全</strong>的双重考虑。</p>
<p>为了性能和内存考虑，采用了共享字符串的做法，但是字符串共享本身并不是线程安全的，因此为了安全使用final关键字修饰使其不可变。</p>
<h3 id="String的几种创建方式有什么不同？"><a href="#String的几种创建方式有什么不同？" class="headerlink" title="String的几种创建方式有什么不同？"></a>String的几种创建方式有什么不同？</h3><h4 id="创建显式String常量"><a href="#创建显式String常量" class="headerlink" title="创建显式String常量"></a>创建显式String常量</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String s = <span class="string">&quot;a&quot;</span>;</span><br></pre></td></tr></table></figure>
<p>分为两种情况：</p>
<ol>
<li>字符串常量池中不存在”a”：将“a“存入字符串常量池中</li>
<li>字符串常量池中已经存有”a”：不再加入”a”</li>
</ol>
<p>因此执行该代码时，虚拟机会先去字符串常量池查看该String常量是否已经存在，若不存在，加入；若存在，则不作变化。 <code>s</code> 则表示对“a“的引用，存放在虚拟机栈中。</p>
<h4 id="创建String对象"><a href="#创建String对象" class="headerlink" title="创建String对象"></a>创建String对象</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String s = <span class="keyword">new</span> String(<span class="string">&quot;a&quot;</span>);</span><br></pre></td></tr></table></figure>
<p>首先都要创建一个String对象，存放在Java堆中，然后同样分为两种情况：</p>
<ol>
<li>字符串常量池中不存在”a”：将“a“存入字符串常量池中</li>
<li>字符串常量池中已经存有”a”：不再加入”a”</li>
</ol>
<p>因此执行该行代码时，首先在Java堆中创建一个String对象，然后同样的，虚拟机会先去字符串常量池查看该String常量是否已经存在，若不存在，加入；若存在，则不作变化。<code>s</code> 则是表示对String对象的引用，String对象存放在Java堆中，<code>s</code> 存放在虚拟机栈中。</p>
<p>例如如下四行代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">String s1 = <span class="keyword">new</span> String(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">String s2 = <span class="keyword">new</span> String(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">String s3 = <span class="string">&quot;a&quot;</span>;</span><br><span class="line">String s4 = <span class="string">&quot;a&quot;</span>;</span><br></pre></td></tr></table></figure>
<p>用图来表示这个引用关系的区别（基于jdk6以后的虚拟机内存分布）</p>
<p><img src="https://typora-yayako.oss-cn-hangzhou.aliyuncs.com/image-20200325113549202.png" class="lazyload" data-srcset="https://typora-yayako.oss-cn-hangzhou.aliyuncs.com/image-20200325113549202.png" srcset="" alt="image-20200325113549202"></p>
<p>所以比较四者的地址是否相等，答案也是显而易见的</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(s1 == s2);	<span class="comment">// false</span></span><br><span class="line">System.out.println(s1 == s3);	<span class="comment">// false</span></span><br><span class="line">System.out.println(s3 == s4);	<span class="comment">// true</span></span><br><span class="line">System.out.println(System.identityHashCode(s1));    <span class="comment">// 460141958</span></span><br><span class="line">System.out.println(System.identityHashCode(s2));    <span class="comment">// 1163157884</span></span><br><span class="line">System.out.println(System.identityHashCode(s3));    <span class="comment">// 1956725890</span></span><br><span class="line">System.out.println(System.identityHashCode(s4));    <span class="comment">// 1956725890</span></span><br></pre></td></tr></table></figure>


<h3 id="String拼接字符串底层原理-“-”"><a href="#String拼接字符串底层原理-“-”" class="headerlink" title="String拼接字符串底层原理 “+”"></a>String拼接字符串底层原理 “+”</h3><p>String是Java中一个不可变的类，一旦创建String对象，其值就不能再改变，那么我们平时进行的字符串拼接做了什么呢？</p>
<p>下面通过<strong>反编译class文件</strong>进行分析。</p>
<p>对于以下代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">String a = <span class="string">&quot;a&quot;</span>;</span><br><span class="line">String b = <span class="string">&quot;b&quot;</span>;</span><br><span class="line">String s1 = <span class="string">&quot;ab&quot;</span>;</span><br><span class="line">String s2 = <span class="string">&quot;a&quot;</span> + <span class="string">&quot;b&quot;</span>;</span><br><span class="line">String s3 = a + b;</span><br><span class="line">String s4 = a + <span class="string">&quot;b&quot;</span>;</span><br></pre></td></tr></table></figure>
<p>查看其反编译结果</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">String var1 &#x3D; &quot;a&quot;;</span><br><span class="line">String var2 &#x3D; &quot;b&quot;;</span><br><span class="line">String var3 &#x3D; &quot;ab&quot;;</span><br><span class="line">String var4 &#x3D; &quot;ab&quot;;</span><br><span class="line">(new StringBuilder()).append(var1).append(var2).toString();</span><br><span class="line">String var6 &#x3D; var1 + &quot;b&quot;;</span><br></pre></td></tr></table></figure>
<h4 id="拼接两个显示String常量"><a href="#拼接两个显示String常量" class="headerlink" title="拼接两个显示String常量"></a>拼接两个显示String常量</h4><p>我们观察到对于 <code>String s2 = &quot;a&quot; + &quot;b&quot;;</code> ，其源码与 <code>String s1 = &quot;ab&quot;;</code> 并无区别，并且在常量池中只出现了“ab”，并未出现单个“a”或者“b”。</p>
<p>这说明：<strong>在编译阶段就已经对两个字符串常量进行了拼接</strong>，相当于最后直接编译 <code>String s = &quot;ab&quot;</code>，也就是编译优化算法中的常量优化（刚开始上编译课）。</p>
<p>可以比较 <code>s1</code> 、<code>s2</code> 的虚拟机地址来证明这点。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">String s1 = <span class="string">&quot;ab&quot;</span>;</span><br><span class="line">String s2 = <span class="string">&quot;a&quot;</span> + <span class="string">&quot;b&quot;</span>;</span><br><span class="line">System.out.println(s1 == s2);	<span class="comment">// true</span></span><br><span class="line">System.out.println(System.identityHashCode(s1));	<span class="comment">// 460141958</span></span><br><span class="line">System.out.println(System.identityHashCode(s2));	<span class="comment">// 460141958</span></span><br></pre></td></tr></table></figure>
<h4 id="拼接两个String对象"><a href="#拼接两个String对象" class="headerlink" title="拼接两个String对象"></a>拼接两个String对象</h4><p>查看反编译文件我们会发现，在编译 <code>String s3 = a + b;</code> 时，是通过新建<strong>StringBuilder</strong>对象并调用其<strong>append</strong>方法进行拼接的，最后再使用<strong>toString</strong>转化为String。</p>
<p>并且常量池中并未出现“ab”。</p>
<p>我们也可以通过对比 <code>s1</code>、<code>s3</code> 的虚拟机地址来证明</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">String a = <span class="string">&quot;a&quot;</span>;</span><br><span class="line">String b = <span class="string">&quot;b&quot;</span>;</span><br><span class="line">String s1 = <span class="string">&quot;ab&quot;</span>;</span><br><span class="line">String s2 = a + b;</span><br><span class="line">System.out.println(s1 == s2);	<span class="comment">// false</span></span><br><span class="line">System.out.println(System.identityHashCode(s1));	<span class="comment">// 460141958</span></span><br><span class="line">System.out.println(System.identityHashCode(s2));	<span class="comment">// 1163157884</span></span><br></pre></td></tr></table></figure>
<p>也就是说用“+”每拼接一次String对象，<strong>都会另外产生一个新的String对象并存放至Java堆里</strong>，这也是为什么不建议在多次拼接字符串的场景下使用String对象的原因。</p>
<h4 id="拼接一个String变量和一个显式String常量"><a href="#拼接一个String变量和一个显式String常量" class="headerlink" title="拼接一个String变量和一个显式String常量"></a>拼接一个String变量和一个显式String常量</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">String s4 = s1 + <span class="string">&quot;b&quot;</span>;</span><br></pre></td></tr></table></figure>
<p>我们通过javap命令查看反编译文件。发现和上面一样，会先新建一个StringBuilder对象，再分别从本地变量和<strong>常量池</strong>中取出用于拼接的两个字符串值，并分别调用StringBuilder的append方法进行连接，最后再调用toString方法转化为String。</p>
<p>另外应该注意到，字符串常量“b”是从常量池中取出的。</p>
   <figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;* 新建一个StringBuilder对象 *&#x2F;</span><br><span class="line">36: new           #7                  &#x2F;&#x2F; class java&#x2F;lang&#x2F;StringBuilder</span><br><span class="line">39: dup</span><br><span class="line">40: invokespecial #8                  &#x2F;&#x2F; Method java&#x2F;lang&#x2F;StringBuilder.&quot;&lt;init&gt;&quot;:()V</span><br><span class="line"></span><br><span class="line">&#x2F;* 加载第一个拼接对象，调用append进行拼接 *&#x2F;</span><br><span class="line">43: aload_1</span><br><span class="line">44: invokevirtual #9                  &#x2F;&#x2F; Method java&#x2F;lang&#x2F;StringBuilder.append:(Ljava&#x2F;lang&#x2F;String;)Ljava&#x2F;lang&#x2F;StringBuilder;</span><br><span class="line"></span><br><span class="line">&#x2F;* 从常量池中取出第二个拼接对象 c，调用append进行拼接 *&#x2F;</span><br><span class="line">47: ldc           #11                 &#x2F;&#x2F; String b</span><br><span class="line">49: invokevirtual #9                  &#x2F;&#x2F; Method java&#x2F;lang&#x2F;StringBuilder.append:(Ljava&#x2F;lang&#x2F;String;)Ljava&#x2F;lang&#x2F;StringBuilder;</span><br><span class="line"></span><br><span class="line">&#x2F;* 调用toString方法转换为String对象，并存入本地变量 *&#x2F;</span><br><span class="line">52: invokevirtual #10                 &#x2F;&#x2F; Method java&#x2F;lang&#x2F;StringBuilder.toString:()Ljava&#x2F;lang&#x2F;String;</span><br><span class="line">55: astore        5</span><br></pre></td></tr></table></figure>
<p>同样的我们可以证明 <code>s4</code> 也是一个存在于堆内的String对象</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">String s1 = <span class="string">&quot;ab&quot;</span>;</span><br><span class="line">String s3 = a + b;</span><br><span class="line">String s4 = a + <span class="string">&quot;b&quot;</span>;</span><br><span class="line">System.out.println(s1 == s4);	<span class="comment">// false</span></span><br><span class="line">System.out.println(s1 == s3);	<span class="comment">// false</span></span><br><span class="line">System.out.println(System.identityHashCode(s1));	<span class="comment">// 460141958</span></span><br><span class="line">System.out.println(System.identityHashCode(s3));	<span class="comment">// 1163157884</span></span><br><span class="line">System.out.println(System.identityHashCode(s4));	<span class="comment">// 1956725890</span></span><br></pre></td></tr></table></figure>
<h4 id="特殊：拼接使用final修饰的常量"><a href="#特殊：拼接使用final修饰的常量" class="headerlink" title="特殊：拼接使用final修饰的常量"></a>特殊：拼接使用final修饰的常量</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> String s1 = <span class="string">&quot;a&quot;</span>;</span><br><span class="line"><span class="keyword">final</span> String s2 = <span class="string">&quot;b&quot;</span>;</span><br><span class="line"><span class="keyword">final</span> String s3 = <span class="keyword">new</span> String(<span class="string">&quot;b&quot;</span>);</span><br><span class="line">String s4 = s1 + s2;</span><br><span class="line">String s5 = s1 + s3;</span><br><span class="line">System.out.println(s4 == <span class="string">&quot;ab&quot;</span>); <span class="comment">// true</span></span><br><span class="line">System.out.println(s5 == <span class="string">&quot;ab&quot;</span>); <span class="comment">// false</span></span><br></pre></td></tr></table></figure>
<p>查看其反编译结果</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">String var3 = <span class="keyword">new</span> String(<span class="string">&quot;b&quot;</span>);</span><br><span class="line">String var4 = <span class="string">&quot;ab&quot;</span>; <span class="comment">// 对应 String s4 = s1 + s2;</span></span><br><span class="line">String var5 = <span class="string">&quot;a&quot;</span> + var3; <span class="comment">// 对应 String s5 = s1 + s3;</span></span><br><span class="line">System.out.println(var4 == <span class="string">&quot;ab&quot;</span>);</span><br><span class="line">System.out.println(var5 == <span class="string">&quot;ab&quot;</span>);</span><br></pre></td></tr></table></figure>
<p>我们发现用如果“+”连接的String变量如果被final修饰，则也起到和连接字符常量一样的结果。毕竟String本身就是一个被final修饰的不可改变的类。<code>String s4 = s1 + s2;</code> 与 <code>String s4 = &quot;a&quot; + &quot;b&quot;;</code> 无异。此时创建的s4正是一个指向字符串常量池中“ab”的引用。</p>
<h4 id="为什么不建议在for循环中使用”-”进行字符串拼接？"><a href="#为什么不建议在for循环中使用”-”进行字符串拼接？" class="headerlink" title="为什么不建议在for循环中使用”+”进行字符串拼接？"></a>为什么不建议在for循环中使用”+”进行字符串拼接？</h4><p>我们通过“+”来对字符串进行拼接，其实并没有改变被拼接String对象的值，而是创建了一个新的String对象。那么也就是说，当我们处于一个需要对字符串进行频繁修改的场景时（尤其是循环），若选择使用“+”来对String对象进行拼接，则会在堆中产生大量无用的String对象。</p>
<p>例如，假设有 <code>String s = new String(&quot;a&quot;);</code> 在前，那么 <code>s = s + &quot;b&quot;;</code> 则是实现了如下图的变化：</p>
<p><img src="https://typora-yayako.oss-cn-hangzhou.aliyuncs.com/image-20200325113736809.png" class="lazyload" data-srcset="https://typora-yayako.oss-cn-hangzhou.aliyuncs.com/image-20200325113736809.png" srcset="" alt="image-20200325113736809"></p>
<p>当然，在非重复执行的单一表达式中使用字符串拼接没有性能损失。</p>
<h2 id="String与StringBuilder与StringBuffer的区别"><a href="#String与StringBuilder与StringBuffer的区别" class="headerlink" title="String与StringBuilder与StringBuffer的区别"></a>String与StringBuilder与StringBuffer的区别</h2><h3 id="String"><a href="#String" class="headerlink" title="String"></a>String</h3><h4 id="文档"><a href="#文档" class="headerlink" title="文档"></a>文档</h4><blockquote>
<p>The <code>String</code> class represents character strings. All string literals in Java programs, such as <code>&quot;abc&quot;</code>, are implemented as instances of this class.</p>
</blockquote>
<p>String类表示字符串。Java程序中的所有字符串文字都是该类的实例。</p>
<blockquote>
<p>Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings. Because String objects are immutable they can be shared. </p>
</blockquote>
<p>字符串是常量，他们的值自创建后就不能被改变。StringBuffer支持改变字符串。而正因为String对象是不可变类，他们可以被共享。</p>
<blockquote>
<p>The class <code>String</code> includes methods for examining individual characters of the sequence, for comparing strings, for searching strings, for extracting substrings, and for creating a copy of a string with all characters translated to uppercase or to lowercase. Case mapping is based on the Unicode Standard version specified by the <code>Character</code> class.</p>
</blockquote>
<p>String类包括对字符串的检查、比较、查找、提取子串、创建大写/小写化副本。大小写映射基于Character类指定的Unicode标准版本。</p>
<blockquote>
<p>The Java language provides special support for the string concatenation operator ( + ), and for conversion of other objects to strings. String concatenation is implemented through the <code>StringBuilder</code>(or <code>StringBuffer</code>) class and its <code>append</code> method. String conversions are implemented through the method <code>toString</code>, defined by <code>Object</code> and inherited by all classes in Java. For additional information on string concatenation and conversion, see Gosling, Joy, and Steele, <em>The Java Language Specification</em>.</p>
</blockquote>
<p>Java为字符串拼接运算符（+）以及将其他对象转换为字符串提供了特殊支持。字符串拼接运算符（+）是通过 <code>StringBuilder</code> 或 <code>StringBuffer</code> 类以及他们各自的 <code>append</code> 方法实现的。字符串转换是通过<code>toString</code> 方法实现的，该方法由 <code>Object</code> 定义并由Java中的所有类继承 。更多额外的字符串拼接、转换信息，请参见Gosling, Joy, and Steele, <em>The Java Language Specification</em>。</p>
<blockquote>
<p>Unless otherwise noted, passing a <code>null</code> argument to a constructor or method in this class will cause a <code>NullPointerException</code> to be thrown.</p>
</blockquote>
<p>一般情况下，给构造方法或其他类方法传递null参会引发空指针异常。</p>
<blockquote>
<p>A <code>String</code> represents a string in the UTF-16 format in which <em>supplementary characters</em> are represented by <em>surrogate pairs</em> (see the section Unicode Character Representations in the <code>Character</code> class for more information). Index values refer to <code>char</code> code units, so a supplementary character uses two positions in a <code>String</code>.</p>
</blockquote>
<p>String表示utf-16格式的字符串，他的补充字符由代理对表示（有关更多信息，请参见Character类中的Unicode字符表示部分。）。索引值是指字符码单位，因此补充字符在String中占两个位置。</p>
<blockquote>
<p>The <code>String</code> class provides methods for dealing with Unicode code points (i.e., characters), in addition to those for dealing with Unicode code units (i.e., <code>char</code> values).</p>
</blockquote>
<p>除了用于处理Unicode代码单元（即char）的方法外，String类还提供了处理Unicode代码点的方法（即字符）。</p>
<h4 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h4><ul>
<li>不可变类</li>
<li>可共享</li>
<li>“+”由StringBuilder/StringBuffer实现</li>
</ul>
<h3 id="StringBuilder"><a href="#StringBuilder" class="headerlink" title="StringBuilder"></a>StringBuilder</h3><h4 id="文档-1"><a href="#文档-1" class="headerlink" title="文档"></a>文档</h4><blockquote>
<p>A mutable sequence of characters. This class provides an API compatible with <code>StringBuffer</code>, but with no guarantee of synchronization. This class is designed for use as a drop-in replacement for <code>StringBuffer</code> in places where the string buffer was being used by a single thread (as is generally the case). Where possible, it is recommended that this class be used in preference to <code>StringBuffer</code> as it will be faster under most implementations.</p>
</blockquote>
<p><strong>可变</strong>的字符序列。提供了与StringBuffer兼容的api，但是没有使用synchronize（即不能保证同步，<strong>线程不安全</strong>）。通常情况下在单线程中充当StringBuffer的角色来使用。在尽可能的情况下，优先使用使用StringBuilder，因为他的方法实现会比使用StringBuffer<strong>更加快速</strong>。</p>
<blockquote>
<p>The principal operations on a <code>StringBuilder</code> are the <code>append</code> and <code>insert</code> methods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string builder. The <code>append</code> method always adds these characters at the end of the builder; the <code>insert</code> method adds the characters at a specified point.</p>
</blockquote>
<p>StringBuilder最主要的操作是 <code>append</code> 和 <code>insert</code> ，他们会被重载以接受各种类型的数据。每个给定的值都会被有效的转换成String并进行 <code>append</code> 或 <code>insert</code> 。<code>append</code> 通常将其加在尾部，<code>insert</code> 则加到指定的位置。</p>
<blockquote>
<p>Every string builder has a capacity. As long as the length of the character sequence contained in the string builder does not exceed the capacity, it is not necessary to allocate a new internal buffer. If the internal buffer overflows, it is automatically made larger.</p>
</blockquote>
<p>每个StringBuilder都有一个容量。只有当字符串长度超出这个容量时，才会分配新的内存缓冲区。</p>
<blockquote>
<p>Instances of <code>StringBuilder</code> are not safe for use by multiple threads. If such synchronization is required then it is recommended that <code>StringBuffer</code> be used.</p>
</blockquote>
<p>StringBuilder对象不是线程安全的。如果需要进行同步锁的话建议使用StringBuffer。</p>
<blockquote>
<p>Unless otherwise noted, passing a <code>null</code> argument to a constructor or method in this class will cause a <code>NullPointerException</code> to be thrown.</p>
</blockquote>
<p>在一般情况下，向构造方法或其他方法中传null参将会报空指针异常。</p>
<h4 id="总结-2"><a href="#总结-2" class="headerlink" title="总结"></a>总结</h4><ul>
<li>可变类</li>
<li>线程不安全</li>
<li>方法执行效率高</li>
</ul>
<h3 id="StringBuffer"><a href="#StringBuffer" class="headerlink" title="StringBuffer"></a>StringBuffer</h3><h4 id="文档-2"><a href="#文档-2" class="headerlink" title="文档"></a>文档</h4><blockquote>
<p>A thread-safe, mutable sequence of characters. A string buffer is like a <code>String</code>, but can be modified. At any point in time it contains some particular sequence of characters, but the length and content of the sequence can be changed through certain method calls.</p>
</blockquote>
<p>线程安全的字符序列。相当于可修改的String类。StringBuffer在任何时间点都持有一些特定的字符序列，但是容量和内容可以通过某些方法改变。</p>
<blockquote>
<p>String buffers are safe for use by multiple threads. The methods are synchronized where necessary so that all the operations on any particular instance behave as if they occur in some serial order that is consistent with the order of the method calls made by each of the individual threads involved.</p>
</blockquote>
<p>StringBuffer是线程安全的。他的方法在必要时会进行同步，以便使其在任何特定实例上的操作顺序与其被调用顺序一致。</p>
<blockquote>
<p>The principal operations on a <code>StringBuffer</code> are the <code>append</code> and <code>insert</code> methods, which are overloaded so as to accept data of any type. Each effectively converts a given datum to a string and then appends or inserts the characters of that string to the string buffer. The <code>append</code> method always adds these characters at the end of the buffer; the <code>insert</code> method adds the characters at a specified point.</p>
</blockquote>
<p>（这段和StringBuilder一样）StringBuffer最主要的操作是 <code>append</code> 和 <code>insert</code> ，他们会被重载以接受各种类型的数据。每个给定的值都会被有效的转换成String并进行 <code>append</code> 或 <code>insert</code> 。<code>append</code> 通常将其加在尾部，<code>insert</code> 则加到指定的位置。</p>
<blockquote>
<p>Whenever an operation occurs involving a source sequence (such as appending or inserting from a source sequence), this class synchronizes only on the string buffer performing the operation, not on the source. Note that while <code>StringBuffer</code> is designed to be safe to use concurrently from multiple threads, if the constructor or the <code>append</code> or <code>insert</code> operation is passed a source sequence that is shared across threads, the calling code must ensure that the operation has a consistent and unchanging view of the source sequence for the duration of the operation. This could be satisfied by the caller holding a lock during the operation’s call, by using an immutable source sequence, or by not sharing the source sequence across threads.</p>
</blockquote>
<p>当对源序列进行操作时，只会在执行操作的StringBuffer上同步，而非源序列。但是要注意的是，尽管StringBuffer是线程安全的，但是如果构造方法或 <code>append</code> 方法或 <code> insert</code> 方法传递的源序列在线程之间共享，则该部分代码必须确保在执行期间对源序列具有一致且不变的看法？这可以通过在调用期间持有锁，使用不可变的源序列或者不可被线程共享的源序列实现。</p>
<blockquote>
<p>Every string buffer has a capacity. As long as the length of the character sequence contained in the string buffer does not exceed the capacity, it is not necessary to allocate a new internal buffer array. If the internal buffer overflows, it is automatically made larger.</p>
</blockquote>
<p>（这段和StringBuilder一样）每个StringBuffer都有一个容量。只有当字符串长度超出这个容量时，才会分配新的内存缓冲区。</p>
<blockquote>
<p>Unless otherwise noted, passing a <code>null</code> argument to a constructor or method in this class will cause a <code>NullPointerException</code> to be thrown.</p>
</blockquote>
<p>（这段和StringBuilder一样）在一般情况下，向构造方法或其他方法中传null参将会报空指针异常。</p>
<blockquote>
<p>As of release JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread, <code>StringBuilder</code>. The <code>StringBuilder</code> class should generally be used in preference to this one, as it supports all of the same operations but it is faster, as it performs no synchronization.</p>
</blockquote>
<p>从jdk5开始，StringBuilder类出现了，他类似于单线程下的StringBuffer。通常情况下应优先使用StringBuilder，因为他持有与StringBuffer相同的方法，且不执行任何同步，相较而言各方法的实现会更加快速。</p>
<h4 id="总结-3"><a href="#总结-3" class="headerlink" title="总结"></a>总结</h4><ul>
<li>可变类</li>
<li>线程安全</li>
<li>方法执行效率次于StringBuilder</li>
</ul>
<h3 id="场景对比"><a href="#场景对比" class="headerlink" title="场景对比"></a>场景对比</h3><p><strong>String：</strong>不频繁修改字符串</p>
<p><strong>StringBuilder：</strong>频繁修改字符串 + 单线程</p>
<p><strong>StringBuffer：</strong>频繁修改字符串 + 多线程</p>

  
  
    
    <div class='footer'>
      
      
      
        <div class='copyright'>
          <blockquote>
            
              
                <p>博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</p>

              
            
              
                <p>本文永久链接是：<a href=https://www.yayako.cn/2020/01/22/%E6%9C%89%E5%85%B3String%E7%9A%84%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%EF%BC%88%E7%BB%93%E5%90%88JVM%EF%BC%89/>https://www.yayako.cn/2020/01/22/%E6%9C%89%E5%85%B3String%E7%9A%84%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%EF%BC%88%E7%BB%93%E5%90%88JVM%EF%BC%89/</a></p>
              
            
          </blockquote>
        </div>
      
      
    </div>
  
  
    


  <div class='article-meta' id="bottom">
    <div class='new-meta-box'>
      
        
          <div class="new-meta-item date" itemprop="dateUpdated" datetime="2021-01-22T22:39:38+08:00">
  <a class='notlink'>
    <i class="fas fa-edit fa-fw" aria-hidden="true"></i>
    <p>更新于：2021年1月22日</p>
  </a>
</div>

        
      
        
          
  
  <div class="new-meta-item meta-tags"><a class="tag" href="/tags/Java/" rel="nofollow"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i><p>Java</p></a></div> <div class="new-meta-item meta-tags"><a class="tag" href="/tags/String/" rel="nofollow"><i class="fas fa-hashtag fa-fw" aria-hidden="true"></i><p>String</p></a></div>


        
      
        
          
  <div class="new-meta-item share -mob-share-list">
  <div class="-mob-share-list share-body">
    
      
        <a class="-mob-share-qq" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=https://www.yayako.cn/2020/01/22/%E6%9C%89%E5%85%B3String%E7%9A%84%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%EF%BC%88%E7%BB%93%E5%90%88JVM%EF%BC%89/&title=有关String的几个问题（结合JVM） - Yayako's Blog&summary=为什么String是不可变的？String的几种创建方式有什么不同？“+”拼接String的底层原理？String与StringBuilder与StringBuffer的区别？"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qq.png" class="lazyload" data-srcset="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qq.png" srcset="">
          
        </a>
      
    
      
        <a class="-mob-share-qzone" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="https://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_onekey?url=https://www.yayako.cn/2020/01/22/%E6%9C%89%E5%85%B3String%E7%9A%84%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%EF%BC%88%E7%BB%93%E5%90%88JVM%EF%BC%89/&title=有关String的几个问题（结合JVM） - Yayako's Blog&summary=为什么String是不可变的？String的几种创建方式有什么不同？“+”拼接String的底层原理？String与StringBuilder与StringBuffer的区别？"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qzone.png" class="lazyload" data-srcset="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/qzone.png" srcset="">
          
        </a>
      
    
      
        <a class="-mob-share-weibo" title="" rel="external nofollow noopener noreferrer noopener"
          
          target="_blank" href="http://service.weibo.com/share/share.php?url=https://www.yayako.cn/2020/01/22/%E6%9C%89%E5%85%B3String%E7%9A%84%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%EF%BC%88%E7%BB%93%E5%90%88JVM%EF%BC%89/&title=有关String的几个问题（结合JVM） - Yayako's Blog&summary=为什么String是不可变的？String的几种创建方式有什么不同？“+”拼接String的底层原理？String与StringBuilder与StringBuffer的区别？"
          
          >
          
            <img src="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/weibo.png" class="lazyload" data-srcset="https://cdn.jsdelivr.net/gh/volantis-x/cdn-org/logo/128/weibo.png" srcset="">
          
        </a>
      
    
      
    
      
    
  </div>
</div>



        
      
    </div>
  </div>


  
  

  
    <div class="prev-next">
      
        <a class='prev' href='/2020/02/28/%E5%89%91%E6%8C%87offer-03.%E6%95%B0%E7%BB%84%E4%B8%AD%E9%87%8D%E5%A4%8D%E7%9A%84%E6%95%B0%E5%AD%97/'>
          <p class='title'><i class="fas fa-chevron-left" aria-hidden="true"></i>剑指offer-03.数组中重复的数字</p>
          <p class='content'>题目难度  简单
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0～n-1 的范围内。数组中某些数字是重复的，但不知道有几个数字重复了，也不知道每个数字重复了几次...</p>
        </a>
      
      
        <a class='next' href='/2020/01/22/Arrays-sort-%E6%BA%90%E7%A0%81%E8%A7%A3%E8%AF%BB-%E6%B5%81%E7%A8%8B%E5%9B%BE%E6%80%BB%E7%BB%93/'>
          <p class='title'>Arrays.sort()源码解读+流程图总结<i class="fas fa-chevron-right" aria-hidden="true"></i></p>
          <p class='content'>Arrays.sort()
1public static void sort(int[] a)

Sorts the specified array into ascending numeric...</p>
        </a>
      
    </div>
  
</article>


  

  <article class="post white-box reveal shadow" id="comments">
    <p ct><i class='fas fa-comments'></i> 评论</p>
    
    <div id="valine_container" class="valine_thread">
  <i class="fas fa-cog fa-spin fa-fw fa-2x"></i>
</div>

  </article>






</div>
<aside class='l_side'>
  
  
    
    



  <section class="widget toc-wrapper shadow desktop mobile" id="toc-div" >
    
  <header>
    
      <i class="fas fa-list fa-fw" aria-hidden="true"></i><span class='name'>本文目录</span>
    
  </header>


    <div class='content'>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%9C%89%E5%85%B3String%E7%9A%84%E5%87%A0%E4%B8%AA%E9%97%AE%E9%A2%98%EF%BC%88%E7%BB%93%E5%90%88JVM%EF%BC%89"><span class="toc-text">有关String的几个问题（结合JVM）</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88String%E6%98%AF%E4%B8%8D%E5%8F%AF%E5%8F%98%E7%9A%84%EF%BC%9F"><span class="toc-text">为什么String是不可变的？</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%80%8E%E4%B9%88%E7%9F%A5%E9%81%93%E5%AE%83%E6%98%AF%E4%B8%8D%E5%8F%AF%E5%8F%98%E7%9A%84%EF%BC%9F"><span class="toc-text">怎么知道它是不可变的？</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E7%94%A8final%E4%BF%AE%E9%A5%B0%EF%BC%9F"><span class="toc-text">为什么要用final修饰？</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E7%94%A8final%E4%BF%AE%E9%A5%B0%E5%9F%9F"><span class="toc-text">用final修饰域</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E7%94%A8final%E4%BF%AE%E9%A5%B0%E7%B1%BB"><span class="toc-text">用final修饰类</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%80%BB%E7%BB%93"><span class="toc-text">总结</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#String%E7%9A%84%E5%87%A0%E7%A7%8D%E5%88%9B%E5%BB%BA%E6%96%B9%E5%BC%8F%E6%9C%89%E4%BB%80%E4%B9%88%E4%B8%8D%E5%90%8C%EF%BC%9F"><span class="toc-text">String的几种创建方式有什么不同？</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E6%98%BE%E5%BC%8FString%E5%B8%B8%E9%87%8F"><span class="toc-text">创建显式String常量</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9B%E5%BB%BAString%E5%AF%B9%E8%B1%A1"><span class="toc-text">创建String对象</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#String%E6%8B%BC%E6%8E%A5%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%BA%95%E5%B1%82%E5%8E%9F%E7%90%86-%E2%80%9C-%E2%80%9D"><span class="toc-text">String拼接字符串底层原理 “+”</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%8B%BC%E6%8E%A5%E4%B8%A4%E4%B8%AA%E6%98%BE%E7%A4%BAString%E5%B8%B8%E9%87%8F"><span class="toc-text">拼接两个显示String常量</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%8B%BC%E6%8E%A5%E4%B8%A4%E4%B8%AAString%E5%AF%B9%E8%B1%A1"><span class="toc-text">拼接两个String对象</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%8B%BC%E6%8E%A5%E4%B8%80%E4%B8%AAString%E5%8F%98%E9%87%8F%E5%92%8C%E4%B8%80%E4%B8%AA%E6%98%BE%E5%BC%8FString%E5%B8%B8%E9%87%8F"><span class="toc-text">拼接一个String变量和一个显式String常量</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%89%B9%E6%AE%8A%EF%BC%9A%E6%8B%BC%E6%8E%A5%E4%BD%BF%E7%94%A8final%E4%BF%AE%E9%A5%B0%E7%9A%84%E5%B8%B8%E9%87%8F"><span class="toc-text">特殊：拼接使用final修饰的常量</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%BA%E4%BB%80%E4%B9%88%E4%B8%8D%E5%BB%BA%E8%AE%AE%E5%9C%A8for%E5%BE%AA%E7%8E%AF%E4%B8%AD%E4%BD%BF%E7%94%A8%E2%80%9D-%E2%80%9D%E8%BF%9B%E8%A1%8C%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%8B%BC%E6%8E%A5%EF%BC%9F"><span class="toc-text">为什么不建议在for循环中使用”+”进行字符串拼接？</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#String%E4%B8%8EStringBuilder%E4%B8%8EStringBuffer%E7%9A%84%E5%8C%BA%E5%88%AB"><span class="toc-text">String与StringBuilder与StringBuffer的区别</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#String"><span class="toc-text">String</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%96%87%E6%A1%A3"><span class="toc-text">文档</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%80%BB%E7%BB%93-1"><span class="toc-text">总结</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#StringBuilder"><span class="toc-text">StringBuilder</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%96%87%E6%A1%A3-1"><span class="toc-text">文档</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%80%BB%E7%BB%93-2"><span class="toc-text">总结</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#StringBuffer"><span class="toc-text">StringBuffer</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%96%87%E6%A1%A3-2"><span class="toc-text">文档</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%80%BB%E7%BB%93-3"><span class="toc-text">总结</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9C%BA%E6%99%AF%E5%AF%B9%E6%AF%94"><span class="toc-text">场景对比</span></a></li></ol></li></ol>
    </div>
  </section>


  


</aside>


   <!--此文件用来存放一些不方便取值的变量-->
<!--思路大概是将值藏到重加载的区域内-->

<script>
  window.pdata={}
  pdata.ispage=true;
  pdata.postTitle="有关String的几个问题（结合JVM）";
  pdata.commentPath="";
  pdata.commentPlaceholder="";

  var l_header=document.getElementById("l_header");
  
  l_header.classList.remove("show");
  
</script>
 
        </div>
        
  
  <footer class="footer clearfix">
    <br><br>
    
      
        <div class="aplayer-container">
          


        </div>
      
    
      
        <br>
        <div class="social-wrapper">
          
            
          
            
          
            
          
        </div>
      
    
      
        <div><p>博客内容遵循 <a target="_blank" rel="noopener" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh">署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议</a></p>
</div>
      
    
      
        
          <div><p><span id="lc-sv">本站总访问量为 <span id='number'><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span> 次</span> <span id="lc-uv">访客数为 <span id='number'><i class="fas fa-circle-notch fa-spin fa-fw" aria-hidden="true"></i></span> 人</span></p>
</div>
        
      
    
      
        
      
    
      
        <div class='copyright'>
        <p><a href="/about/">Copyright © 2021 扭秧歌的一只泱</a> <strong>|</strong> <img src="https://yayako-blog.oss-cn-hangzhou.aliyuncs.com/settings/%E5%A4%87%E6%A1%88%E5%9B%BE%E6%A0%87.png" class="lazyload" data-srcset="https://yayako-blog.oss-cn-hangzhou.aliyuncs.com/settings/%E5%A4%87%E6%A1%88%E5%9B%BE%E6%A0%87.png" srcset="" alt="备案图标"> <a target="_blank" rel="noopener" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=33032402002017">浙公网安备33032402002017号</a> <strong>|</strong> <a target="_blank" rel="noopener" href="https://beian.miit.gov.cn/#/Integrated/index">浙ICP备20015559号</a></p>

        </div>
      
    
  </footer>


        <a
          id="s-top"
          class="fas fa-arrow-up fa-fw"
          href="javascript:void(0)"
        ></a>
      </div>
    </div>
    <div>
      <script>
window.volantis={};
window.volantis.loadcss=document.getElementById("loadcss");
/********************脚本懒加载函数********************************/
function loadScript(src, cb) {
var HEAD = document.getElementsByTagName('head')[0] || document.documentElement;
var script = document.createElement('script');
script.setAttribute('type','text/javascript');
if (cb) script.onload = cb;
script.setAttribute('src', src);
HEAD.appendChild(script);
}
//https://github.com/filamentgroup/loadCSS
var loadCSS = function( href, before, media, attributes ){
	var doc = window.document;
	var ss = doc.createElement( "link" );
	var ref;
	if( before ){
		ref = before;
	}
	else {
		var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes;
		ref = refs[ refs.length - 1];
	}
	var sheets = doc.styleSheets;
	if( attributes ){
		for( var attributeName in attributes ){
			if( attributes.hasOwnProperty( attributeName ) ){
				ss.setAttribute( attributeName, attributes[attributeName] );
			}
		}
	}
	ss.rel = "stylesheet";
	ss.href = href;
	ss.media = "only x";
	function ready( cb ){
		if( doc.body ){
			return cb();
		}
		setTimeout(function(){
			ready( cb );
		});
	}
	ready( function(){
		ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) );
	});
	var onloadcssdefined = function( cb ){
		var resolvedHref = ss.href;
		var i = sheets.length;
		while( i-- ){
			if( sheets[ i ].href === resolvedHref ){
				return cb();
			}
		}
		setTimeout(function() {
			onloadcssdefined( cb );
		});
	};
	function loadCB(){
		if( ss.addEventListener ){
			ss.removeEventListener( "load", loadCB );
		}
		ss.media = media || "all";
	}
	if( ss.addEventListener ){
		ss.addEventListener( "load", loadCB);
	}
	ss.onloadcssdefined = onloadcssdefined;
	onloadcssdefined( loadCB );
	return ss;
};
</script>
<script>
  
  loadCSS("https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@5.14/css/all.min.css", window.volantis.loadcss);
  
  
  
  
</script>
<!-- required -->

<script src="https://cdn.jsdelivr.net/npm/jquery@3.5/dist/jquery.min.js"></script>

<script>
  function pjax_fancybox() {
    $(".md .gallery").find("img").each(function () { //渲染 fancybox
      var element = document.createElement("a"); // a 标签
      $(element).attr("class", "fancybox");
      $(element).attr("pjax-fancybox", "");  // 过滤 pjax
      $(element).attr("href", $(this).attr("src"));
      if ($(this).attr("data-original")) {
        $(element).attr("href", $(this).attr("data-original"));
      }
      $(element).attr("data-fancybox", "images");
      var caption = "";   // 描述信息
      if ($(this).attr('alt')) {  // 判断当前页面是否存在描述信息
        $(element).attr('data-caption', $(this).attr('alt'));
        caption = $(this).attr('alt');
      }
      var div = document.createElement("div");
      $(div).addClass("fancybox");
      $(this).wrap(div); // 最外层套 div ，其实主要作用还是 class 样式
      var span = document.createElement("span");
      $(span).addClass("image-caption");
      $(span).text(caption); // 加描述
      $(this).after(span);  // 再套一层描述
      $(this).wrap(element);  // 最后套 a 标签
    })
    $(".md .gallery").find("img").fancybox({
      selector: '[data-fancybox="images"]',
      hash: false,
      loop: false,
      closeClick: true,
      helpers: {
        overlay: {closeClick: true}
      },
      buttons: [
        "zoom",
        "close"
      ]
    });
  };
  function SCload_fancybox() {
    if ($(".md .gallery").find("img").length == 0) return;
    loadCSS("https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.css", document.getElementById("loadcss"));
    setTimeout(function() {
      loadScript('https://cdn.jsdelivr.net/gh/fancyapps/fancybox@3.5.7/dist/jquery.fancybox.min.js', pjax_fancybox)
    }, 1);
  };
  $(function () {
    SCload_fancybox();
  });
</script>


<!-- internal -->

  
  
  
    
<script src="https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js"></script>

    <script type="text/javascript">
        var imgs=["https://yayako-blog.oss-cn-hangzhou.aliyuncs.com/cover/hd.jpg"];
        if ('false' == 'true') {
          function shuffle(arr){
            /*From countercurrent-time*/
            var n = arr.length;
            while(n--) {
              var index = Math.floor(Math.random() * n);
              var temp = arr[index];
              arr[index] = arr[n];
              arr[n] = temp;
            }
          }
          shuffle(imgs);
        }
	  function Pjax_backstretch(){
        
          $.backstretch(
            imgs,
          {
            duration: "10000",
            fade: "1500"
          });
        
	  }
	  loadScript("https://cdn.jsdelivr.net/npm/jquery-backstretch@2.1.18/jquery.backstretch.min.js",Pjax_backstretch)
    </script>
  




<script>
  function loadIssuesJS() {
    if ($(".md").find(".issues-api").length == 0) return;
    setTimeout(function() {
	
	  loadScript('/js/issues.js');
	
    }, 1);
  };
  $(function () {
    loadIssuesJS();
  });
</script>



  <script defer src="https://cdn.jsdelivr.net/npm/vanilla-lazyload@17.1.0/dist/lazyload.min.js"></script>
<script>
  // https://www.npmjs.com/package/vanilla-lazyload
  // Set the options globally
  // to make LazyLoad self-initialize
  window.lazyLoadOptions = {
    elements_selector: ".lazyload",
    threshold: 0
  };
  // Listen to the initialization event
  // and get the instance of LazyLoad
  window.addEventListener(
    "LazyLoad::Initialized",
    function (event) {
      window.lazyLoadInstance = event.detail.instance;
    },
    false
  );
  document.addEventListener('DOMContentLoaded', function () {
    lazyLoadInstance.update();
  });
  document.addEventListener('pjax:complete', function () {
    lazyLoadInstance.update();
  });
</script>




  
  
    <script>
      window.FPConfig = {
        delay: 0,
        ignoreKeywords: [],
        maxRPS: 5,
        hoverDelay: 25
      };
    </script>
    <script defer src="https://cdn.jsdelivr.net/gh/gijo-varghese/flying-pages@2.1.2/flying-pages.min.js"></script>
  




  <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
    var clipboard = new ClipboardJS('.btn-copy', {
        target: function (trigger) {
            return trigger.nextElementSibling
        }
    });
    function wait(callback, seconds) {
        var timelag = null;
        timelag = window.setTimeout(callback, seconds)
    }
    function pjax_initCopyCode() {
		if($(".highlight .code pre").length+$(".article pre code").length==0)return;
        var copyHtml = '';
        copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
        copyHtml += '<i class="fas fa-copy"></i><span>COPY</span>';
        copyHtml += '</button>';
        $(".highlight .code pre").before(copyHtml);
        $(".article pre code").before(copyHtml);
        clipboard.off('success').on('success', function (e) {
            let $btn = $(e.trigger);
            $btn.addClass('copied');
            let $icon = $($btn.find('i'));
            $icon.removeClass('fa-copy');
            $icon.addClass('fa-check-circle');
            let $span = $($btn.find('span'));
            $span[0].innerText = 'COPIED';
            wait(function () {
                $icon.removeClass('fa-check-circle');
                $icon.addClass('fa-copy');
                $span[0].innerText = 'COPY'
            }, 2000)
        });
        clipboard.off('error').on('error', function (e) {
            e.clearSelection();
            let $btn = $(e.trigger);
            $btn.addClass('copy-failed');
            let $icon = $($btn.find('i'));
            $icon.removeClass('fa-copy');
            $icon.addClass('fa-times-circle');
            let $span = $($btn.find('span'));
            $span[0].innerText = 'COPY FAILED';
            wait(function () {
                $icon.removeClass('fa-times-circle');
                $icon.addClass('fa-copy');
                $span[0].innerText = 'COPY'
            }, 2000)
        })
    }
    $(function () {
        pjax_initCopyCode()
    });
</script>




   <script src="https://cdn.jsdelivr.net/npm/scrollreveal@4.0.6/dist/scrollreveal.min.js"></script>
<script type="text/javascript">
  function pjax_scrollrebeal() {
    ScrollReveal().reveal('.l_main .reveal', {
      distance: '32px',
      duration: '800',
      interval: '20',
      scale: '1',
      easing: 'ease-out'
    });
  }
  $(function () {
    pjax_scrollrebeal();
  });
</script>






  
  
<script src="https://cdn.jsdelivr.net/npm/valine@1.4/dist/Valine.min.js"></script>


<script>
  function emoji(path, idx, ext) {
    return path + "/" + path + "-" + idx + "." + ext;
  }
  var emojiMaps = {};
  for (var i = 1; i <= 54; i++) {
    emojiMaps['tieba-' + i] = emoji('tieba', i, 'png');
  }
  for (var i = 1; i <= 101; i++) {
    emojiMaps['qq-' + i] = emoji('qq', i, 'gif');
  }
  for (var i = 1; i <= 116; i++) {
    emojiMaps['aru-' + i] = emoji('aru', i, 'gif');
  }
  for (var i = 1; i <= 125; i++) {
    emojiMaps['twemoji-' + i] = emoji('twemoji', i, 'png');
  }
  for (var i = 1; i <= 4; i++) {
    emojiMaps['weibo-' + i] = emoji('weibo', i, 'png');
  }
  function pjax_valine() {
    if(!document.querySelectorAll("#valine_container")[0])return;
    let pagePlaceholder = pdata.commentPlaceholder || "快来评论吧~";
    let path = pdata.commentPath;
    if (path.length == 0) {
      let defaultPath = '';
      path = defaultPath || decodeURI(window.location.pathname);
    }
    var valine = new Valine();
    valine.init(Object.assign({"js":"https://cdn.jsdelivr.net/npm/valine@1.4/dist/Valine.min.js","path":null,"placeholder":"快来评论吧~","appId":"crkoKrezsl4I7UWQjAvOy2N1-gzGzoHsz","appKey":"OUvAeTn1CnCmGFdNYVH00G0S","meta":["nick","mail","link"],"requiredFields":["nick","mail"],"enableQQ":true,"recordIP":false,"avatar":"robohash","pageSize":10,"lang":"zh-cn","highlight":true,"visitor":true,"mathJax":false}, {
      el: '#valine_container',
      path: path,
      placeholder: pagePlaceholder,
      emojiCDN: 'https://cdn.jsdelivr.net/gh/volantis-x/cdn-emoji/valine/',
      emojiMaps: emojiMaps,
    }))
  }
  $(function () {
    pjax_valine();
  });
</script>





  
<script src="/js/app.js"></script>



<!-- optional -->

  <script>
const SearchServiceimagePath="https://cdn.jsdelivr.net/gh/volantis-x/cdn-volantis@master/img/";
const ROOT =  ("/" || "/").endsWith('/') ? ("/" || "/") : ("//" || "/" );

$('.input.u-search-input').one('focus',function(){
	
		loadScript('/js/search/hexo.js',setSearchService);
	
})

function listenSearch(){
  
    customSearch = new HexoSearch({
      imagePath: SearchServiceimagePath
    });
  
}
function setSearchService() {
	listenSearch();
	
		document.addEventListener("pjax:success", listenSearch);
	
}
</script>











  <script defer>

  const LCCounter = {
    app_id: 'u9j57bwJod4EDmXWdxrwuqQT-MdYXbMMI',
    app_key: 'jfHtEKVE24j0IVCGHbvuFClp',
    custom_api_server: '',

    // 查询存储的记录
    getRecord(Counter, url, title) {
      return new Promise(function (resolve, reject) {
        Counter('get', '/classes/Counter?where=' + encodeURIComponent(JSON.stringify({url})))
          .then(resp => resp.json())
          .then(({results, code, error}) => {
            if (code === 401) {
              throw error;
            }
            if (results && results.length > 0) {
              var record = results[0];
              resolve(record);
            } else {
              Counter('post', '/classes/Counter', {url, title: title, times: 0})
                .then(resp => resp.json())
                .then((record, error) => {
                  if (error) {
                    throw error;
                  }
                  resolve(record);
                }).catch(error => {
                console.error('Failed to create', error);
                reject(error);
              });
            }
          }).catch((error) => {
          console.error('LeanCloud Counter Error:', error);
          reject(error);
        });
      })
    },

    // 发起自增请求
    increment(Counter, incrArr) {
      return new Promise(function (resolve, reject) {
        Counter('post', '/batch', {
          "requests": incrArr
        }).then((res) => {
          res = res.json();
          if (res.error) {
            throw res.error;
          }
          resolve(res);
        }).catch((error) => {
          console.error('Failed to save visitor count', error);
          reject(error);
        });
      });
    },

    // 构建自增请求体
    buildIncrement(objectId) {
      return {
        "method": "PUT",
        "path": `/1.1/classes/Counter/${ objectId }`,
        "body": {
          "times": {
            '__op': 'Increment',
            'amount': 1
          }
        }
      }
    },

    // 校验是否为有效的 UV
    validUV() {
      var key = 'LeanCloudUVTimestamp';
      var flag = localStorage.getItem(key);
      if (flag) {
        // 距离标记小于 24 小时则不计为 UV
        if (new Date().getTime() - parseInt(flag) <= 86400000) {
          return false;
        }
      }
      localStorage.setItem(key, new Date().getTime().toString());
      return true;
    },

    addCount(Counter) {
      var enableIncr = '' === 'true' && window.location.hostname !== 'localhost';
      enableIncr = true;
      var getterArr = [];
      var incrArr = [];
      // 请求 PV 并自增
      var pvCtn = document.querySelector('#lc-sv');
      if (pvCtn || enableIncr) {
        var pvGetter = this.getRecord(Counter, 'https://www.yayako.cn' + '/#lc-sv', 'Visits').then((record) => {
          incrArr.push(this.buildIncrement(record.objectId))
          var eles = document.querySelectorAll('#lc-sv #number');
          if (eles.length > 0) {
            eles.forEach((el,index,array)=>{
              el.innerText = record.times + 1;
              if (pvCtn) {
                pvCtn.style.display = 'inline';
              }
            })
          }
        });
        getterArr.push(pvGetter);
      }

      // 请求 UV 并自增
      var uvCtn = document.querySelector('#lc-uv');
      if (uvCtn || enableIncr) {
        var uvGetter = this.getRecord(Counter, 'https://www.yayako.cn' + '/#lc-uv', 'Visitors').then((record) => {
          var vuv = this.validUV();
          vuv && incrArr.push(this.buildIncrement(record.objectId))
          var eles = document.querySelectorAll('#lc-uv #number');
          if (eles.length > 0) {
            eles.forEach((el,index,array)=>{
              el.innerText = record.times + (vuv ? 1 : 0);
              if (uvCtn) {
                uvCtn.style.display = 'inline';
              }
            })
          }
        });
        getterArr.push(uvGetter);
      }

      // 请求文章的浏览数，如果是当前页面就自增
      var allPV = document.querySelectorAll('#lc-pv');
      if (allPV.length > 0 || enableIncr) {
        for (i = 0; i < allPV.length; i++) {
          let pv = allPV[i];
          let title = pv.getAttribute('data-title');
          var url = 'https://www.yayako.cn' + pv.getAttribute('data-path');
          if (url) {
            var viewGetter = this.getRecord(Counter, url, title).then((record) => {
              // 是当前页面就自增
              let curPath = window.location.pathname;
              if (curPath.includes('index.html')) {
                curPath = curPath.substring(0, curPath.lastIndexOf('index.html'));
              }
              if (pv.getAttribute('data-path') == curPath) {
                incrArr.push(this.buildIncrement(record.objectId));
              }
              if (pv) {
                var ele = pv.querySelector('#lc-pv #number');
                if (ele) {
                  if (pv.getAttribute('data-path') == curPath) {
                    ele.innerText = (record.times || 0) + 1;
                  } else {
                    ele.innerText = record.times || 0;
                  }
                  pv.style.display = 'inline';
                }
              }
            });
            getterArr.push(viewGetter);
          }
        }
      }

      // 如果启动计数自增，批量发起自增请求
      if (enableIncr) {
        Promise.all(getterArr).then(() => {
          incrArr.length > 0 && this.increment(Counter, incrArr);
        })
      }

    },


    fetchData(api_server) {
      var Counter = (method, url, data) => {
        return fetch(`${ api_server }/1.1${ url }`, {
          method,
          headers: {
            'X-LC-Id': this.app_id,
            'X-LC-Key': this.app_key,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(data)
        });
      };
      this.addCount(Counter);
    },

    refreshCounter() {
      var api_server = this.app_id.slice(-9) !== '-MdYXbMMI' ? this.custom_api_server : `https://${ this.app_id.slice(0, 8).toLowerCase() }.api.lncldglobal.com`;
      if (api_server) {
        this.fetchData(api_server);
      } else {
        fetch('https://app-router.leancloud.cn/2/route?appId=' + this.app_id)
          .then(resp => resp.json())
          .then(({api_server}) => {
            this.fetchData('https://' + api_server);
          });
      }
    }

  };

  LCCounter.refreshCounter();

  document.addEventListener('pjax:complete', function () {
    LCCounter.refreshCounter();
  });
</script>








<script>
function listennSidebarTOC() {
  const navItems = document.querySelectorAll(".toc li");
  if (!navItems.length) return;
  const sections = [...navItems].map((element) => {
    const link = element.querySelector(".toc-link");
    const target = document.getElementById(
      decodeURI(link.getAttribute("href")).replace("#", "")
    );
    link.addEventListener("click", (event) => {
      event.preventDefault();
      window.scrollTo({
		top: target.offsetTop + 100,
		
		behavior: "smooth"
		
	  });
    });
    return target;
  });

  function activateNavByIndex(target) {
    if (target.classList.contains("active-current")) return;

    document.querySelectorAll(".toc .active").forEach((element) => {
      element.classList.remove("active", "active-current");
    });
    target.classList.add("active", "active-current");
    let parent = target.parentNode;
    while (!parent.matches(".toc")) {
      if (parent.matches("li")) parent.classList.add("active");
      parent = parent.parentNode;
    }
  }

  function findIndex(entries) {
    let index = 0;
    let entry = entries[index];
    if (entry.boundingClientRect.top > 0) {
      index = sections.indexOf(entry.target);
      return index === 0 ? 0 : index - 1;
    }
    for (; index < entries.length; index++) {
      if (entries[index].boundingClientRect.top <= 0) {
        entry = entries[index];
      } else {
        return sections.indexOf(entry.target);
      }
    }
    return sections.indexOf(entry.target);
  }

  function createIntersectionObserver(marginTop) {
    marginTop = Math.floor(marginTop + 10000);
    let intersectionObserver = new IntersectionObserver(
      (entries, observe) => {
        let scrollHeight = document.documentElement.scrollHeight + 100;
        if (scrollHeight > marginTop) {
          observe.disconnect();
          createIntersectionObserver(scrollHeight);
          return;
        }
        let index = findIndex(entries);
        activateNavByIndex(navItems[index]);
      },
      {
        rootMargin: marginTop + "px 0px -100% 0px",
        threshold: 0,
      }
    );
    sections.forEach((element) => {
      element && intersectionObserver.observe(element);
    });
  }
  createIntersectionObserver(document.documentElement.scrollHeight);
}

document.addEventListener("DOMContentLoaded", listennSidebarTOC);
document.addEventListener("pjax:success", listennSidebarTOC);
</script>

<!-- more -->

  


<script src="https://cdn.jsdelivr.net/npm/pjax@0.2.8/pjax.min.js"></script>


<script>
    var pjax;
    document.addEventListener('DOMContentLoaded', function () {
      pjax = new Pjax({
        elements: 'a[href]:not([href^="#"]):not([href="javascript:void(0)"]):not([pjax-fancybox])',
        selectors: [
          "title",
          "#l_cover",
          "#pjax-container",
          "#pjax-header-nav-list"
        ],
        cacheBust: false,   // url 地址追加时间戳，用以避免浏览器缓存
        timeout: 5000
      });
    });

    document.addEventListener('pjax:send', function (e) {
      //window.stop(); // 相当于点击了浏览器的停止按钮

      try {
        var currentUrl = window.location.pathname;
        var targetUrl = e.triggerElement.href;
        var banUrl = [""];
        if (banUrl[0] != "") {
          banUrl.forEach(item => {
            if(currentUrl.indexOf(item) != -1 || targetUrl.indexOf(item) != -1) {
              window.location.href = targetUrl;
            }
          });
        }
      } catch (error) {}

      window.subData = null; // 移除标题（用于一二级导航栏切换处）
      if (typeof $.fancybox != "undefined") {
        $.fancybox.close();    // 关闭弹窗
      }
      volantis.$switcher.removeClass('active'); // 关闭移动端激活的搜索框
      volantis.$header.removeClass('z_search-open'); // 关闭移动端激活的搜索框
      volantis.$wrapper.removeClass('sub'); // 跳转页面时关闭二级导航

      // 解绑事件 避免重复监听
      volantis.$topBtn.unbind('click');
      $('.menu a').unbind('click');
      $(window).unbind('resize');
      $(window).unbind('scroll');
      $(document).unbind('scroll');
      $(document).unbind('click');
      $('body').unbind('click');
	  
    });

    document.addEventListener('pjax:complete', function () {
      // 关于百度统计对 SPA 页面的处理：
      // 方案一：百度统计>管理>单页应用设置中，打开开启按钮即可对SPA进行统计。 https://tongji.baidu.com/web/help/article?id=324
      // 方案二：取消注释下列代码。 https://tongji.baidu.com/web/help/article?id=235
       

      // 关于谷歌统计对 SPA 页面的处理：
      // 当应用以动态方式加载内容并更新地址栏中的网址时，也应该更新通过 gtag.js 存储的网页网址。
      // https://developers.google.cn/analytics/devguides/collection/gtagjs/single-page-applications?hl=zh-cn
      


      $('.nav-main').find('.list-v').not('.menu-phone').removeAttr("style",""); // 移除小尾巴的移除
      $('.menu-phone.list-v').removeAttr("style",""); // 移除小尾巴的移除
      $('script[data-pjax], .pjax-reload script').each(function () {
        $(this).parent().append($(this).remove());
      });
      try{
          if (typeof $.fancybox == "undefined") {
            SCload_fancybox();
          } else {
            pjax_fancybox();
          }
        
		  Pjax_backstretch()
        
        
        
          pjax_scrollrebeal();
        
        if (typeof IssuesAPI == "undefined") {
          loadIssuesJS();
        }
        
          pjax_initCopyCode();
        
        
          pjax_valine();
        
        
        
        
        
        
      } catch (e) {
        console.log(e);
      }
	  
    });

    document.addEventListener('pjax:error', function (e) {
	  
      window.location.href = e.triggerElement.href;
    });
</script>
 
    </div>
  </body>
</html>
